﻿//=======================================================
// 作者：LR
// 公司：广州纷享科技发展有限公司
// 描述：
// 创建时间：2021-06-21 14:12:20
//=======================================================
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Accessibility;
using UnityEngine.UI;

namespace Sailfish
{
    public enum KeyType
    {
        KeyDown,
        KeyStay,
        KeyExit
    }

  
    public class InputKey
    {
        bool inDown;
        bool inStay;
        bool inExit;

        public bool isPass = false;


        string key;

        InputData inputData;

        /// <summary>
        /// info事件对应的数组
        /// </summary>
        List<MethodInfo> downMethods = new List<MethodInfo>();
        List<MethodInfo> stayMethods = new List<MethodInfo>();
        List<MethodInfo> exitMethods = new List<MethodInfo>();


        public InputKey(InputData inputData, string key)
        {
            this.key = key;
            this.inputData = inputData;
            CallUnit.updateDontDestroyCall.AddListener(Update);
        }

        bool CheckCondition()
        {
            return (inputData.playerID == InputMethod.m_PlayerID)&& isPass;
        }

        


        void DownEvents()
        {
            for (int i = 0; i < downMethods.Count; i++)
            {
                downMethods[i].Invoke(inputData.instance, null);
            }
        }

        void StayEvents()
        {
            for (int i = 0; i < stayMethods.Count; i++)
            {
                stayMethods[i].Invoke(inputData.instance, null);
            }
        }

        void UpEvents()
        {
            for (int i = 0; i < exitMethods.Count; i++)
            {
                exitMethods[i].Invoke(inputData.instance, null);
            }
        }

        bool IsHas()
        {
            if (exitMethods.Count <= 0 && stayMethods.Count <= 0)
                return false;
            return true;
        }

        public void AddMethodInfo(KeyType type, MethodInfo info)
        {
            if (type == KeyType.KeyDown)
                downMethods.Add(info);
            else if (type == KeyType.KeyStay)
                stayMethods.Add(info);
            else if(type == KeyType.KeyExit)
                exitMethods.Add(info);
        }


        public void Update()
        {
            if (CheckCondition())
            {
                if (inStay)
                {
                    StayEvents();
                }

                if (!inDown)
                {
                    inDown = true;
                    inStay = true;

                    DownEvents();
                }
            }
            else
            {
                if (inDown && !inExit)
                {
                    inExit = true;
                    UpEvents();

                    Replace();
                }
            }
        }


        public void Replace()
        {
            inDown = false;
            inStay = false;
            inExit = false;

            isPass = false;
        }

        public void RemoveAll()
        {
            for (int i = 0; i < downMethods.Count; i++)
            {
                downMethods[i] = null;
            }
            downMethods.Clear();

            for (int i = 0; i < stayMethods.Count; i++)
            {
                stayMethods[i] = null;
            }
            stayMethods.Clear();

            for (int i = 0; i < exitMethods.Count; i++)
            {
                exitMethods[i] = null;
            }
            exitMethods.Clear();

            CallUnit.updateDontDestroyCall.RemoveListener(Update);
        }
    }


    public class InputMethod
    {
        public static string Key2Str(KeyCode key)=> $"Code.{key}";
  
        public static List<int> playerIDs = new List<int>();
        public static List<InputKey> keyInputs = new List<InputKey>();

        public static Dictionary<int, InputData> intInputs = new Dictionary<int, InputData>();
        public static Dictionary<string, List<int>> keyCodeInputs = new Dictionary<string, List<int>>();

        public static int playerID = 0;

        static List<KeyCode> keyCodes = new List<KeyCode>();

        /// <summary>
        /// AfterAssembliesLoaded
        /// </summary>
        /// <param name="playerID"></param>
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
        public static void Init()
        { 
            CallUnit.updateDontDestroyCall.AddListener(Check);
        }

        public static void Check()
        {
#if ENABLE_LEGACY_INPUT_MANAGER

            if (Input.anyKeyDown)
            {
                if (Input.GetKeyDown(KeyCode.Tab))
                {
                    playerID++;
                    if (playerID >= playerIDs.Count)
                        playerID = 0;
                }

                //按下的什么键位
                foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode)))
                {
                    if (Input.GetKeyDown(keyCode))
                    {
                        InputDownTrigger(keyCode, playerID);
                        keyCodes.Add(keyCode);
                    }
                }
            }

            if (keyCodes.Count > 0)
            {
                var temps = new List<KeyCode>();
                foreach (var item in keyCodes)
                {
                    if (Input.GetKeyUp(item))
                    {
                        InputExitTrigger(item, playerID);
                        temps.Add(item);
                    }
                }

                if (temps.Count > 0)
                {
                    foreach (var item in temps)
                    {
                        keyCodes.Remove(item);
                    }
                    temps.Clear();
                }
            }
                //按下的什么键位
#else
          

#endif

        }


        /// <summary>
        /// 按不同的KEY，触发对用事件
        /// </summary>
        /// <param name="keyCode">按键</param>
        public static void InputDownTrigger(KeyCode keyCode, int playerID = 0)
        {
            var keyStr = InputMethod.Key2Str(keyCode);

            if (keyCodeInputs.TryGetValue(keyStr, out List<int> values))
            {
                foreach (var item in values)
                {
                    if (intInputs.TryGetValue(item, out InputData input))
                    {
                        if (input.playerID == playerID) input.TriggerInfos(keyStr);
                    }
                }
            }
        }


        /// <summary>
        /// 按不同的KEY，触发对用事件
        /// </summary>
        /// <param name="keyCode">按键</param>
        public static void InputExitTrigger(KeyCode keyCode, int playerID = 0)
        {
            var keyStr = InputMethod.Key2Str(keyCode);

            if (keyCodeInputs.TryGetValue(keyStr, out List<int> values))
            {
                foreach (var item in values)
                {
                    if (intInputs.TryGetValue(item, out InputData input))
                    {
                        if (input.playerID == playerID) input.ExitInfos(keyStr);
                    }
                }
            }
        }


        public static int m_PlayerID = 0;

    }

    /// <summary>
    /// Input数据
    /// </summary>
    public class InputData
    {
        public Dictionary<string, InputKey> keyInfos = new Dictionary<string, InputKey>();

        internal int    playerID;// 脚本分配的玩家ID
        internal object instance;// 脚本的实例

        /// <summary>
        /// 缓存脚本实例
        /// </summary>
        public InputData(object target, int playerID)
        {
            instance      = target;
            this.playerID = playerID;
        }


       
     

        /// <summary>
        /// 添加input控制事件
        /// </summary>
        public void AddInfos(string keyStr, KeyType keyType, MethodInfo info)
        {
      
            if (keyInfos.ContainsKey(keyStr) == false)
            {
                InputKey temp = new InputKey(this, keyStr);
                keyInfos.Add(keyStr, temp);
                InputMethod.keyInputs.Add(temp);
            }
            keyInfos[keyStr].AddMethodInfo(keyType, info);
        }


        /// <summary>
        /// 触发对应key的函数
        /// </summary>
        /// <param name="key"></param>
        public void TriggerInfos(string key)
        {
            if (keyInfos.TryGetValue(key, out InputKey input))
            {
                input.isPass = true;
            }
        }



        /// <summary>
        /// 退出对应key的函数
        /// </summary>
        /// <param name="key"></param>
        public void ExitInfos(string key)
        {
            if (keyInfos.TryGetValue(key, out InputKey input))
            {
                input.isPass = false;
            }
        }


        /// <summary>
        /// 删除所有的info函数
        /// </summary>
        public void RemoveAllInfos()
        {
            foreach (var item in keyInfos.Values)
            {
                item.RemoveAll();
            }
            keyInfos.Clear();
            instance = null;
        }
    }

    /// <summary>
    /// 输出输入函数特性
    /// </summary>
    public class InputAttribute : System.Attribute
    {

        /// <summary>
        /// 触发的KEY
        /// </summary>
        public string keyStr = string.Empty;

        /// <summary>
        /// 触发KEY的状态
        /// </summary>
        public KeyType keyType;


        #region 构造函数
        /// <summary>
        /// 构造函数不同的用法
        /// </summary>
        public InputAttribute() 
        {
            keyType = KeyType.KeyDown;
        }

        public InputAttribute(KeyCode keyCode) : this(keyCode, KeyType.KeyDown)
        {
            this.keyStr = InputMethod.Key2Str(keyCode);
        }

        public InputAttribute(string keyStr) :   this(keyStr, KeyType.KeyDown)
        {
            this.keyStr = keyStr;
        }


        public InputAttribute(KeyCode keyCode, KeyType keyType)
        {
            this.keyStr  = InputMethod.Key2Str(keyCode);
            this.keyType = keyType;
        }

        public InputAttribute(string keyStr, KeyType keyType)
        {
            this.keyStr  = keyStr;
            this.keyType = keyType;
        }
        #endregion
    }

    /// <summary>
    /// 特性数据处理
    /// </summary>
    public static class InputAttributeManager
    {
        /// <summary>
        /// 加入需要检测按钮事件的脚本
        /// </summary>
        /// <param name="target"></param>
        /// <param name="playerID"></param>
        public static void AddInputEvents(this object target, int playerID = 0)
        {
            var fields = target.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            InputData tempEvent = new InputData(target, playerID);

            for (int i = 0; i < fields.Length; i++)
            {
                InputAttribute[] attrs = fields[i].GetCustomAttributes(typeof(InputAttribute), false) as InputAttribute[];
                if (attrs != null && attrs.Length > 0)
                {
                    tempEvent.AddInfos(attrs[0].keyStr, attrs[0].keyType, fields[i]);

                    Debug.Log("Input实例化按键：" + attrs[0].keyStr.ToString());

                    if (InputMethod.keyCodeInputs.TryGetValue(attrs[0].keyStr, out var values))
                    {
                        values.Add(target.GetHashCode());
                    }
                    else
                    {
                        var temp = new List<int>() { target.GetHashCode() };
                        InputMethod.keyCodeInputs.Add(attrs[0].keyStr, temp);
                    }
                }
            }

            if (!InputMethod.playerIDs.Contains(playerID))
                InputMethod.playerIDs.Add(playerID);


            InputMethod.intInputs.Add(target.GetHashCode(), tempEvent);
        }





        /// <summary>
        /// 移除检测的脚本
        /// </summary>
        /// <param name="target"></param>
        public static void RemoveInputEvents(this object target)
        {
            if (InputMethod.intInputs.ContainsKey(target.GetHashCode()))
            {
                InputMethod.intInputs[target.GetHashCode()].RemoveAllInfos();
                InputMethod.intInputs.Remove(target.GetHashCode());
            }

            foreach (var item in InputMethod.keyCodeInputs.Values)
            {
                if (item.Contains(target.GetHashCode()))
                {
                    item.Remove(target.GetHashCode());
                }
            }
        }
    }
    //-----------------Input 特性---------------end
}
